package com.sunyard.manage.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.util.LinkedList;
import java.util.Queue;
import java.util.TreeMap;

/**
 * @author k3887
 * @Description
 * @date 2018-11-22 下午4:07:19
 */
public class EbcdicGbkConverter {

    /**
     * Description
     */
    private static Logger logger = LoggerFactory.getLogger(EbcdicGbkConverter.class);

    /**
     * Description
     */
    final static String GBK_FILE = "gbk_cvt.txt";
    /**
     * Description
     */
    final static String EBCDIC_CHARSET_NAME = "Cp1047";
    /**
     * Description
     */
    private static EbcdicGbkConverter instance;
    /**
     * Description
     */
    private static TreeMap<Character, Integer> gbk2Ebcdic = new TreeMap<Character, Integer>();
    /**
     * Description
     */
    private static TreeMap<Integer, Character> ebcdic2Gbk = new TreeMap<Integer, Character>();

    /**
     * Description
     */
    private static char[] gbk2EbcdicChar;
    /**
     * Description
     */
    private static int[] gbk2EbcdicInt;

    /**
     * Description
     */
    private static char[] ebcdic2GbkChar;
    /**
     * Description
     */
    private static int[] ebcdic2GbkInt;

    /**
     * Description
     */
    private EbcdicGbkConverter() {
    }

    /**
     * Description
     */
    public static EbcdicGbkConverter getInstance() {

        if (instance == null) {
            synchronized (EbcdicGbkConverter.class) {
                if (instance == null) {
                    instance = new EbcdicGbkConverter();
                    instance.initEbcdicGBKMapping();
                }
            }
        }
        return instance;
    }

    /**
     * Description
     */
    private void initEbcdicGBKMapping() {
        URL url = EbcdicGbkConverter.class.getResource("/" + GBK_FILE);
        InputStream is = null;
        try {
            is = url.openConnection().getInputStream();

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String record;
            logger.debug("初始化编码转换容器");

            while ((record = reader.readLine()) != null) {
                char gbkChar = record.charAt(0);
                String ebcdicChar = record.substring(1, 5);
                Integer ebcdicInt = Integer.valueOf(ebcdicChar, 16);
                gbk2Ebcdic.put(gbkChar, ebcdicInt);
                ebcdic2Gbk.put(ebcdicInt, gbkChar);
            }
            gbk2EbcdicChar = new char[gbk2Ebcdic.size()];
            gbk2EbcdicInt = new int[gbk2Ebcdic.size()];
            int index = 0;
            for (Character c : gbk2Ebcdic.keySet()) {
                gbk2EbcdicChar[index] = c.charValue();
                gbk2EbcdicInt[index++] = gbk2Ebcdic.get(c).intValue();
            }
            ebcdic2GbkChar = new char[ebcdic2Gbk.size()];
            ebcdic2GbkInt = new int[ebcdic2Gbk.size()];

            index = 0;
            for (Integer i : ebcdic2Gbk.keySet()) {
                ebcdic2GbkChar[index] = ebcdic2Gbk.get(i).charValue();
                ebcdic2GbkInt[index++] = i.intValue();
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                is = null;
            }
        }

    }

    /**
     * Description
     */
    private static boolean isEnglishChar(char c) {
        if (c < 0xFF) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * string into EBCDIC format
     */
    public byte[] stringToEbcdic(String data) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        char[] dataChar = data.toCharArray();
        Queue<Integer> gbkQueue = new LinkedList<Integer>();
        String eng;
        for (int i = 0; i < dataChar.length; i++) {
            char c = dataChar[i];
            if (isEnglishChar(c)) {
                if (gbkQueue.size() > 0) {
                    readGBKQueue(out, gbkQueue);
                }
                eng = new String(new char[]{c});
                byte[] buff = eng.getBytes(EBCDIC_CHARSET_NAME);
                out.write(buff);
            } else {
                gbkQueue.add(midSearch(c));
            }
        }
        if (gbkQueue.size() > 0) {
            readGBKQueue(out, gbkQueue);
        }
        byte[] byteArray = out.toByteArray();
        out.close();
        return byteArray;
    }

    /**
     * The Chinese character start with '0x0e' and end with '0x0f' in EBCDIC character set
     *
     * @param out
     * @param queue
     * @throws IOException
     */
    private static void readGBKQueue(OutputStream out, Queue<Integer> queue) throws IOException {
        out.write(0x0e);
        Integer data;
        while ((data = queue.poll()) != null) {
            int dataInt = data.intValue();
            out.write((dataInt >> 8) & 0xff);
            out.write(dataInt & 0xff);
        }
        out.write(0x0f);
    }

    /**
     * Dichotomy to search
     *
     * @param c
     * @return
     */
    private static int midSearch(char c) {
        int start = 0;
        int end = gbk2EbcdicChar.length - 1;
        while (start <= end) {
            int mid = (start + end) >> 1;
            char midChar = gbk2EbcdicChar[mid];
            if (c > midChar) {
                start = mid + 1;
            } else if (c < midChar) {
                end = mid - 1;
            } else {
                return gbk2EbcdicInt[mid];
            }
        }
        return -1;
    }

    /**
     * Dichotomy to search
     *
     * @param c
     * @return
     */
    private static char midSearchEbcdicToGBK(int c) {
        int start = 0;
        int end = ebcdic2GbkInt.length - 1;
        while (start <= end) {
            int mid = (start + end) >> 1;
            int midValue = ebcdic2GbkInt[mid];
            if (c > midValue) {
                start = mid + 1;
            } else if (c < midValue) {
                end = mid - 1;
            } else {
                return ebcdic2GbkChar[mid];
            }
        }
        return ' ';
    }

    /**
     * EBCDIC to GBK format String
     */
    public static String ebcdicToGBK(byte[] data) throws UnsupportedEncodingException {
        Queue<Integer> ebcdicQueue = new LinkedList<Integer>();
        StringBuffer buff = new StringBuffer();
        for (int i = 0; i < data.length; i++) {
            int b = data[i] & 0xff;
            if (b == 0x0e) {
                ebcdicQueue.add(data[++i] & 0xff);
            } else if (b == 0x0f) {
                readEbcdicQueue(buff, ebcdicQueue);
            } else {
                if (ebcdicQueue.size() == 0) {
                    buff.append(new String(new byte[]{data[i]}, EBCDIC_CHARSET_NAME));
                } else {
                    ebcdicQueue.add(data[i] & 0xff);
                }
            }
        }
        return buff.toString();
    }

    /**
     * Description
     */
    private static void readEbcdicQueue(StringBuffer buff, Queue<Integer> ebcdicQueue) {

        Integer data1;
        while ((data1 = ebcdicQueue.poll()) != null) {
            int data2 = ebcdicQueue.poll();
            int data = (data1 << 8) + data2;
            char c = midSearchEbcdicToGBK(data);
            buff.append(c);
        }
    }

    /**
     * Convert the string to 16 Radix format
     */
    public static String str2HexStr(String str) {

        return byte2HexStr(str.getBytes());

    }

    /**
     * Description
     */
    public static String byte2HexStr(byte[] data) {

        char[] chars = "0123456789abcdef".toCharArray();
        StringBuilder sb = new StringBuilder("");
        byte[] bs = data;
        int bit;
        for (int i = 0; i < bs.length; i++) {
            bit = (bs[i] & 0x0f0) >> 4;
            sb.append(chars[bit]);
            bit = bs[i] & 0x0f;
            sb.append(chars[bit]);
            sb.append(" ");
        }
        return sb.toString();

    }

    /**
     * Description
     */
    public static void EBCDICToASCII(String fromfile, String tofile) {
        try {
            FileInputStream in = new FileInputStream(new File(fromfile));
            FileOutputStream out = new FileOutputStream(new File(tofile));
            int tempint;
            String lineTXT = null;
            byte[] tempbytes = new byte[1024];
            while ((tempint = in.read()) != -1) {
                in.read(tempbytes);
                lineTXT = EbcdicGbkConverter.ebcdicToGBK(tempbytes);
                out.write(lineTXT.getBytes());
            }
            in.close();
            out.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * Description
     */
    public static void ASCIIToEBCDIC(String fromfile, String tofile) {
        try {
            InputStreamReader in = new InputStreamReader(new FileInputStream(fromfile), "GBK");
            FileOutputStream out = new FileOutputStream(new File(tofile));
            BufferedReader br = new BufferedReader(in);
            String lineTXT = null;
            while ((lineTXT = br.readLine()) != null) {
                //lineTXT = lineTXT+"\r\n";
                byte[] tempbytes = new EbcdicGbkConverter().stringToEbcdic(lineTXT);
                out.write(tempbytes);
            }
            in.close();
            out.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * Description
     */
    private static void t() throws IOException {
        //new EbcdicGbkConverter();
        String str = "";
        BufferedReader bufferedReader = new BufferedReader(new FileReader("D:/home/cfbatch/cupdFile/mp/1"));
        StringBuffer sb = new StringBuffer();
        String content = null;
        while ((content = bufferedReader.readLine()) != null) {
            content = new String(content.getBytes());
            sb.append(content.trim()).append("\r\n");
        }

        String sbVal = sb.toString();
        int ib = sbVal.lastIndexOf("\r\n");
        sbVal = sbVal.substring(0, ib);

        byte[] b2 = new EbcdicGbkConverter().stringToEbcdic(sbVal);
        OutputStream out = new FileOutputStream("C:/home/cfbatch/cupdFile/mp/2");
        out.write(b2);
        out.flush();

        File file1 = new File("C:/home/cfbatch/cupdFile/mp/2");
        InputStream input = new FileInputStream(file1);
        int size = input.available();
        byte[] by = new byte[size];
        input.read(by);
        str = EbcdicGbkConverter.ebcdicToGBK(by);
        System.out.println("GBK:" + str);

        OutputStream out1 = new FileOutputStream("C:/home/cfbatch/cupdFile/mp/3");
        out1.write(str.getBytes());
        out1.flush();


    }


    /**
     * Description
     */
    public static byte[] File2byte(File file) {
        byte[] buffer = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }


    /**
     * Description
     */
    public static void main(String[] args) throws IOException {
        //System.out.println(EbcdicGbkConverter.class.getResource(""));
        //EbcdicGbkConverter.getInstance();
        //ASCIIToEBCDIC("F:/RM04211703tmp","F:/test");
        EBCDICToASCII("F:/RM04251707", "F:/RM04251707EBCD");
        //int rowNum = 100000;
        //Long skip= 0l;
        //List<String> fileLineList;
        //try {
        //	fileLineList = FileUtil.readFileToList("D:/home/cfbatch/cupdFile/mp/1.txt.bak", skip,
        //			rowNum);
        //	List contentList = new ArrayList<String>();
        //	for (int i = 0; i < fileLineList.size()-1; i++) {
        //		String listContent = fileLineList.get(i);
        //
        //		listContent=listContent.replace("","");//回车  +
        //		System.out.println("listContent="+listContent);
        //		byte[] arr = listContent.getBytes("GBK");
        //		System.out.println("aaaaaa="+new String (arr));
        //		String c = instance.ebcdicToGBK(arr);//+
        //		System.out.println("c="+c);
        //		 arr = c.getBytes("GBK");
        //		contentList.add(listContent);
        //
        //	}
        //
        //	StringBuilder content = new StringBuilder();
        //	if (contentList != null && contentList.size() > 0) {
        //		for (int j = 0; j < contentList.size(); j++) {
        //			Object str = contentList.get(j);
        //			content.append(str == null ? "" : str.toString());
        //			if (j != contentList.size() - 1)
        //				content.append("\r\n");//换行
        //		}
        //
        //
        //			File f = new File("D:/home/cfbatch/cupdFile/mp/2013092511206421_2");
        //			if (f.exists())
        //				f.delete();
        //
        //			byte[] b2 = new EbcdicGbkConverter().stringToEbcdic(content.toString());
        //			OutputStream out = new FileOutputStream("D:/home/cfbatch/cupdFile/bankmds/DTATI.CF0915RA01.BAK");
        //
        //			out.write(b2);
        //			out.flush();
        //	}
        //
        //} catch (Exception e) {
        //	e.printStackTrace();
        //}

    }
}